﻿using Avalon.Core.MVC.Attributes;
using Avalon.Core.Resources;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

namespace Avalon.Core.Data.Templates {
    [EntityDescription(Title = "AvalonEntity_Title", Description = "AvalonEntity_Description", ResourceType = typeof(AvalonResources))]
    public class AvalonEntity : BaseAuditableEntity {
        //Header information
        public string Title { get; set; }
        public string Name { get { return Title; } }
        public string Description { get; set; }
        public string Information { get; set; }

        //System information
        public string EntityTypeName { get; set; }
        public string EntityName { get; set; }
        public string AssemblyQualifiedName { get; set; }
        public string ControllerName { get; set; }
        [Display(Name = "Controller alias")]
        public string ControllerAlias { get; set; }

        [Display(Name = "Entity fields", GroupName = "System information")]
        public virtual ICollection<AvalonEntityField> EntityFields { get; set; }

        //Reference information
        public virtual ICollection<AvalonView> ViewModels { get; set; }
        public virtual ICollection<AvalonLink> Links { get; set; }

        //Navigation information
        public bool IsNavigatable { get; set; }

        //Filter information
        public virtual ICollection<AvalonFilter> Filters { get; set; }

        //Renderer information
        public string ViewRendererGroup { get; set; }



        public AvalonEntity() {
            Filters = new HashSet<AvalonFilter>();
            EntityFields = new HashSet<AvalonEntityField>();
            ViewModels = new HashSet<AvalonView>();
        }


        public static AvalonEntity Create(Type entityType, AvalonEntity entity) {
            //If the entity is null set all the values of the entity. Else use the existing values, 
            //and just look at the properties.
            if (entity == null) {
                entity = new AvalonEntity();

                //Header properties
                entity.Title = entityType.Name;
                entity.Description = string.Empty;
                entity.Information = string.Empty;
                EntityDescriptionAttribute entityDescriptionAttribute = entityType.GetCustomAttribute<EntityDescriptionAttribute>(true);
                if (entityDescriptionAttribute != null) {
                    entity.Title = entityDescriptionAttribute.Title;
                    entity.Description = entityDescriptionAttribute.Description;
                    entity.Information = entityDescriptionAttribute.Description;
                }

                //System properties
                entity.EntityName = entityType.Name;
                entity.EntityTypeName = entityType.FullName;
                entity.AssemblyQualifiedName = entityType.AssemblyQualifiedName;
                entity.ControllerName = entityType.Name;
                entity.ControllerAlias = entityType.Name;
                ControllerNameAttribute controllerNameAttribute = entityType.GetCustomAttribute<ControllerNameAttribute>(true);
                if (controllerNameAttribute != null) {
                    entity.ControllerAlias = controllerNameAttribute.Name;
                }

                //Navigation properties
                entity.IsNavigatable = true;
            }

            //set entity fields
            foreach (PropertyInfo pInfo in entityType.GetProperties()) {
                AvalonEntityField field = entity.EntityFields.FirstOrDefault(ef => ef.Name == pInfo.Name);
                if (field == null) {
                    field = new AvalonEntityField();
                    field.IsParent = false;
                    field.IsChildCollection = false;
                    field.IsIEntity = false;

                    field.Title = pInfo.Name;
                    field.Name = pInfo.Name;
                    DisplayAttribute display = pInfo.GetCustomAttribute<DisplayAttribute>();
                    if (display != null) {
                        field.Title = display.Name;
                        field.GroupName = display.GroupName;
                        field.Order = display.GetOrder() ?? 0;
                    }

                    field.FieldType = FieldType.Text;

                    field.RenderMode = RenderMode.Any;
                    RenderModeAttribute renderMode = pInfo.GetCustomAttribute<RenderModeAttribute>();
                    if (renderMode != null) {
                        field.RenderMode = renderMode.RenderMode;
                    }

                    //If the property is decorated with a parent property
                    ParentAttribute parent = pInfo.GetCustomAttribute<ParentAttribute>();
                    if (parent != null) {
                        field.IsParent = true;
                        field.AssemblyQualifiedName = pInfo.PropertyType.AssemblyQualifiedName;
                    }

                    //If the property is a child collection
                    if (pInfo.PropertyType.IsGenericType && pInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)) {
                        field.IsChildCollection = true;
                        field.AssemblyQualifiedName = pInfo.PropertyType.AssemblyQualifiedName;
                    }

                    //If the property is an ientity
                    if (typeof(IEntity).IsAssignableFrom(pInfo.PropertyType)) {
                        field.IsIEntity = true;
                    }

                    //add the entity field to the entity
                    entity.EntityFields.Add(field);
                }
            }

            return entity;
        }
    }
}
